You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(server): worktree setup no longer hangs when the shell is Nushell - #29461
When the setup terminal's shell is Nushell, worktree setup and settle actions never finish. T3 types a POSIX wrapper, ( <command> ); printf '\n<sentinel>%s\n' "$?", and nu passes "$?" to printf literally, so the terminal prints __T3_SETUP_DONE___<token>:$?, which never matches the numeric completion pattern. When the command fails, nu stops before printf and prints nothing. Either way, a synchronous setup holds the thread forever.
Nu skips the rest of the input after a failing command, so the status print only runs behind try. A failing external reports its own exit code ($e.exit_code). Other errors (command not found, error make) report 1 and are printed, since catch would otherwise hide them.
Like zsh and fish, nu's line editor enables bracketed paste, so the command is still typed only after that signal. The readiness check now names the shells that lack the signal (PowerShell, cmd) instead of the ones that have it.
Scope and approval
https://git.cafe/pingdotgg/t3code/issues/24423 reports this bug; it has not been triaged yet. This is a small, focused fix for an obvious bug: one new case in the existing shell switch, plus keeping nu on the existing line-editor wait. Setup and settle actions, including thread launches from MCP and scheduled tasks, all go through this runner. No contract, client, or docs changes are needed.
Verification
A new case in ProjectSetupScriptRunner.test.ts checks that nu waits for its line editor and receives the Nushell wrapper. It fails on main, where nu gets ( vp i\r); printf … "$?", and passes here. Reverting only the readiness change also fails it, because the command is typed before the line editor starts. vp run check:changed passes.
Real Nushell 0.116.1 in a PTY on macOS, typing the generated wrapper into an interactive login shell with a full user config:
echo setup-ok → 0; node -e "process.exit(7)" → 7
unknown command → 1, with nu's error shown; error make {msg: "boom"} → 1
multi-line commands and a trailing # comment → 0
external stdout stays a TTY
End to end on a dev server (macOS, SHELL set to a Nushell login wrapper, Replay provider), with a synchronous setup action on a scrap repo, via t3 thread start --worktree main --wait:
main echo setup-ok setup terminal: __T3_SETUP_DONE___767656c2…:$? thread start still waiting at the 45s timeout
branch echo setup-ok setup terminal: __T3_SETUP_DONE___45e5f29b…:0 replay turn runs, CLI exits 0
branch node -e "process.exit(3)" "stopped with status failed: … Setup script exited with 3." CLI exits 1
Not checked: Nushell on Linux or Windows, and Nushell versions older than 0.116. Setup actions must still be valid Nushell: POSIX-only syntax such as a && b fails to parse, so no status is printed, the same as a syntax error in a POSIX shell.
Model and harness: Claude Opus 5.5 through Claude Code in T3 Code.
Setup and settle actions typed a POSIX completion wrapper into Nushell,
which passes "$?" to printf literally, so the completion marker never
carried an exit code and synchronous setup waited forever. Nushell now
gets its own wrapper that reports the exit code through try/catch.
Macroscope's review found this PR approvable — Adds Nushell-compatible completion and error reporting for existing project setup scripts, preventing completion tracking from hanging. The change leaves script selection, execution triggers, and other shells’ behavior unchanged.
You can add or adjust custom eligibility rules. Learn more.
It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.
No actionable comments were generated in the recent review.
Walkthrough
The setup runner recognizes Nushell and wraps setup commands with Nushell-specific completion and exit-status handling. It waits for shell readiness before writing the wrapper. Tests cover default and custom commands, including special characters.
The runner maps nu to Nushell and waits for shell readiness before writing a Nushell-specific wrapper. The wrapper runs the command from the worktree, captures its exit status, attempts to remove its temporary script, and emits a completion sentinel. The test harness accepts a custom command and tests readiness and escaping.
Nushell setup commands now report completion and exit status instead of hanging. One narrow limitation remains: setup commands that rely on definitions loaded from Nushell autoload directories will fail in the setup shell, and users can work around it by defining them in config.nu. This does not block merging.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title names the main change: worktree setup no longer hangs when the shell is Nushell. It matches the summary and objectives, which add a Nushell completion wrapper and a readiness change in Proje…
Description check
The description follows the template. It has Problem, Change, Scope, and Verification sections, plus the model and harness line. The Problem section gives the failure mode and reproduction context. Th…
Linked Issues check
Objective [#24423]: with a Nushell setup terminal, the setup command must run and T3 must record its exit status without waiting for a marker that never matches.
All changed hunks connect to the Nushell completion fix for #24423: - ProjectSetupScriptRunner.ts: the nu detection case, the nushell wrapper case, the readiness condition, and the bracketed-pas…
Full details: Title check
Explanation
The title names the main change: worktree setup no longer hangs when the shell is Nushell. It matches the summary and objectives, which add a Nushell completion wrapper and a readiness change in ProjectSetupScriptRunner. The title is a conventional commit with a clear scope, so it is specific enough for a teammate scanning history.
Full details: Description check
Explanation
The description follows the template. It has Problem, Change, Scope, and Verification sections, plus the model and harness line. The Problem section gives the failure mode and reproduction context. The Change section explains the Nushell wrapper and the readiness change. The Scope section links issue 24423 and explains why no triage is needed. The Verification section lists focused tests, real PTY checks on Nushell 0.116.1, an end-to-end run, and unchecked areas. The Problem section does not state an expected behavior explicitly, but the wording implies it. The description is complete enough to pass.
Full details: Linked Issues check
Explanation
Objective [#24423]: with a Nushell setup terminal, the setup command must run and T3 must record its exit status without waiting for a marker that never matches.
Evidence at the reviewed head 893a673 (merge base d8f3fef):
resolveCompletionShell maps nu to a new nushell case. Before this PR, nu mapped to posix.
wrapCommandForCompletion, nushell case: the wrapper sets $env.LAST_EXIT_CODE = 0, creates a temporary .nu file in $env.T3CODE_WORKTREE_PATH, writes the escaped command to it, and runs ^$nu.current-exe --login --commands with cd and source. A catch sets the status to 1 when it is still 0. The wrapper then removes the file and prints \n<sentinel>($__t3c). The sentinel is numeric, so the completion pattern can match it. A failing command does not stop the final print.
Readiness: the condition is now completionShell === "powershell" || completionShell === null for bypassing the wait. Nushell therefore waits for the line editor, as the PR description states.
ProjectSetupScriptRunner.test.ts keeps an escaped Nushell case. It checks that nothing is typed before \x1b[?2004h. It then checks the exact wrapper text for multi-line, quoted, and control-character input.
Differences to record: the PR description shows an inline let __t3c = try { ... } wrapper. The code at head uses the temporary-file wrapper. The description is stale for the wrapper form, so I used the head code as evidence. The PR summary and raw summary also differ from the head code in places. I used the head code as the evidence for this assessment.
Not verified by me: Nushell runtime behavior of mktemp, save --force --raw, source, nested exit-code propagation, and the $env.LAST_EXIT_CODE update after an external failure. The unit test confirms emitted text and the readiness wait, not Nushell runtime results. The PR reports macOS PTY and end-to-end runs, and a comment reports a Linux VM run. I did not reproduce those runs.
The optional explicit project-script shell in #24423 is an alternative, not a requirement. The PR objectives state that it is not in scope.
Full details: Out of Scope Changes check
Explanation
All changed hunks connect to the Nushell completion fix for #24423:
ProjectSetupScriptRunner.ts: the nu detection case, the nushell wrapper case, the readiness condition, and the bracketed-paste comment that lists nu.
ProjectSetupScriptRunner.test.ts: the optional command harness parameter and closeOnSettle stub, which support the Nushell test, and the consolidated Nushell test. The last commit removes a plain and an import-only Nushell case that the escaping case covers.
The temporary .nu file in the worktree is part of the Nushell wrapper. The PR objectives say it keeps relative source and use imports resolved from the worktree. The wrapper removes it after the run and on preparation errors, so this is not unrelated scope.
No contract, client, docs, or unrelated server changes appear in the diffs for these two files. Other changed paths were not inspected.
Finishing Touches
Generate unit tests (beta)
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
Run observed scripts in a child of the current Nushell executable, loading
its login configuration. The parent reports LAST_EXIT_CODE after the child
finishes, including explicit exit commands, without relying on catch-record
fields that differ between Nushell versions.
Extend the existing mocked-terminal test to assert the complete wrapper,
including the failure fallback, sentinel, and quoting of multiline input.
The expanded test fails before the change.
Validation: vp run check:changed passes. Real PTY verification passed 40
cases across Nu 0.97.1, 0.98.0, 0.99.1, and 0.116.1 on macOS:
echo setup-ok -> completion sentinel:0
node -e "process.exit(7)" -> completion sentinel:7
open /t3-review-does-not-exist -> error shown, completion sentinel:1
error make {msg: "boom"} -> error shown, completion sentinel:1
exit 0 -> completion sentinel:0
exit 7 -> completion sentinel:7
Configured commands, multiline input, quoting, and stdin/stdout/stderr TTY
behavior also passed on each version.
Model and harness: GPT-6.1 Sol through Codex in T3 Code.
verified on a linux boat vm with nushell 0.116.1, the dev web client in /usr/bin/google-chrome driven by the repo's playwright, and real codex gpt-6.1-sol turns. created a real git project, imported its synchronous setup action through the client, and launched worktree threads through the running server's t3 thread start --worktree main --wait path. no mocks, fixtures, or replay provider.
github/main at c3cfb5e1e3: setup printed setup-ok and returned to the prompt, but the completion marker ended in literal $?. the client remained on "setting up worktree" and codex did not start. stopped the baseline after reproducing this failure.
original pr head 31a7e9e00d: normal setup completed and codex read the output file. reproduced the review's directory bug with login.nu changing directory: setup reported success but wrote outside the worktree, and codex found the file missing.
fixed and pushed 7f9ac5916e for login-directory changes. further runtime verification found parser-time source resolution still used the login directory. the contributor addressed that in 4f7dc60f184e840e6bcc4c6169c411dee8b1b96d, which is the verified final head.
final head: with login.nu changing directory and a conflicting setup.nu in that directory, source ./setup.nu and use ./module.nu module-ok load the worktree's files. a command defined in config.nu also works. codex reads source-cwd.txt, and its contents match the new worktree and provider cwd.
neighboring behavior: exit 7 fails setup with status 7; error make fails with status 1. neither starts codex. multiline input, quotes, and a trailing comment complete with status 0. settling through the client runs the settle action and emits status 0.
setup runs git add -A: only source-cwd.txt is staged, with no .t3-setup-* file staged or left behind. the temporary script deletes itself before user commands run.
exact final wrapper also exercised directly with nushell 0.97.1: relative source/module imports succeed, exit 7 emits 7, and a native error emits 1. full app verification used 0.116.1.
blacksmith on the final head: vp run check:changed edc37c96c810d42761ab510c6a3e9d0a741547dc passed format, lint, knip, graphql, affected typechecks, and 12 tests in 2 files. cafe reports 28 successful checks and none pending or failing.
inspected the screenshots and recording, and exercised uploaded video playback and seeking.
Not verified
native desktop, ios, android, macos, windows, and scheduled-task launches were not exercised on this linux vm. older nushell received direct wrapper checks rather than a full client/provider pass. no performance comparison is claimed.
Nushell parses --commands before evaluating its leading cd. If login.nu
changes directories, relative source and use statements can select files
from the login directory even when the action runs in the worktree.
Use Nu's mktemp, save, to nuon, source, and rm commands to load the action
from a temporary .nu file in the worktree. Relative imports resolve against
that file's directory, and the child retains the user's login definitions.
Remove the file before the action runs so it does not appear in git status;
clean it up in the parent when parsing fails. Preparation errors still
report a numeric completion status. Guard null catch arguments on older Nu.
Extend the existing mocked-terminal table with source/use input and assert
the complete native wrapper. The expanded assertion fails before the fix.
vp run check:changed passes, including 12 tests in two affected files.
Runtime: 60 PTY cases and eight preparation-failure cases passed on macOS
with Nushell 0.97.1, 0.98.0, 0.99.1, and 0.116.1. Login startup changes cwd;
both directories contain conflicting setup.nu and module.nu files:
before: source ./setup.nu -> SOURCE-WRONG, status 0
before: use ./module.nu *; location -> MODULE-WRONG, status 0
after: source ./setup.nu -> SOURCE-WORKTREE, status 0
after: use ./module.nu *; location -> MODULE-WORKTREE, status 0
file present only in login cwd -> diagnostic, status 1, no leftover
exit 0 / exit 7 / error make -> statuses 0 / 7 / 1
Nested imports, login-defined commands, multiline quoting, paths with spaces
and quotes, TTY input/output, and cleanup passed. Creation and save errors
show diagnostics and report status 1 without running the action.
Model and harness: GPT-6.1 Sol through Codex in T3 Code.
Linux, Nushell 0.117.0, Chromium at 1280×800, light theme, fresh synthetic state. With SHELL pointing directly to Nushell, configure a synchronous worktree setup action, create a worktree thread, and inspect setup and its terminal. Repeat with echo setup-ok and node -e "process.exit(7)".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.
3 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When the setup terminal's shell is Nushell, worktree setup and settle actions never finish. T3 types a POSIX wrapper,
( <command> ); printf '\n<sentinel>%s\n' "$?", and nu passes"$?"toprintfliterally, so the terminal prints__T3_SETUP_DONE___<token>:$?, which never matches the numeric completion pattern. When the command fails, nu stops beforeprintfand prints nothing. Either way, a synchronous setup holds the thread forever.Fixes https://git.cafe/pingdotgg/t3code/issues/24423 (reported on Linux; reproduced here on macOS with T3 Code v0.1.6 and Nushell 0.116.1).
Change
ProjectSetupScriptRunnernow treatsnuas its own completion shell and wraps the action in Nushell syntax:Nu skips the rest of the input after a failing command, so the status print only runs behind
try. A failing external reports its own exit code ($e.exit_code). Other errors (command not found,error make) report 1 and are printed, sincecatchwould otherwise hide them.Like zsh and fish, nu's line editor enables bracketed paste, so the command is still typed only after that signal. The readiness check now names the shells that lack the signal (PowerShell, cmd) instead of the ones that have it.
Scope and approval
https://git.cafe/pingdotgg/t3code/issues/24423 reports this bug; it has not been triaged yet. This is a small, focused fix for an obvious bug: one new case in the existing shell switch, plus keeping nu on the existing line-editor wait. Setup and settle actions, including thread launches from MCP and scheduled tasks, all go through this runner. No contract, client, or docs changes are needed.
Verification
ProjectSetupScriptRunner.test.tschecks that nu waits for its line editor and receives the Nushell wrapper. It fails on main, where nu gets( vp i\r); printf … "$?", and passes here. Reverting only the readiness change also fails it, because the command is typed before the line editor starts.vp run check:changedpasses.echo setup-ok→ 0;node -e "process.exit(7)"→ 7error make {msg: "boom"}→ 1# comment→ 0SHELLset to a Nushell login wrapper, Replay provider), with a synchronous setup action on a scrap repo, viat3 thread start --worktree main --wait:Not checked: Nushell on Linux or Windows, and Nushell versions older than 0.116. Setup actions must still be valid Nushell: POSIX-only syntax such as
a && bfails to parse, so no status is printed, the same as a syntax error in a POSIX shell.Model and harness: Claude Opus 5.5 through Claude Code in T3 Code.
Fixes #24423